/**
 *
 * Roslan's Remake of MOO in 3D using Ardor3D
 * (C) 2011 Roslan Amir. All rights reserved.
 */

package com.roslan.games.moo3d.ui.hud;

import com.ardor3d.extension.ui.UIButton;
import com.ardor3d.extension.ui.UILabel;
import com.ardor3d.extension.ui.UIPanel;
import com.ardor3d.extension.ui.backdrop.SolidBackdrop;
import com.ardor3d.extension.ui.event.ActionEvent;
import com.ardor3d.extension.ui.event.ActionListener;
import com.ardor3d.extension.ui.layout.AnchorLayout;
import com.ardor3d.extension.ui.layout.AnchorLayoutData;
import com.ardor3d.extension.ui.layout.RowLayout;
import com.ardor3d.extension.ui.util.Alignment;
import com.ardor3d.scenegraph.Node;
import com.roslan.games.moo3d.FleetManager;
import com.roslan.games.moo3d.GameData;
import com.roslan.games.moo3d.GameData.Actions;
import com.roslan.games.moo3d.GameData.GameStates;
import com.roslan.games.moo3d.GameManager;
import com.roslan.games.moo3d.data.Fleet;
import com.roslan.games.moo3d.data.ShipType;
import com.roslan.games.moo3d.spatial.FleetNode;
import com.roslan.games.moo3d.spatial.FleetNode.FleetStatus;
import com.roslan.games.moo3d.utils.HexGridUtils;

/**
 * This Panel displays the information about the currently selected Fleet.
 * 
 * @author Roslan Amir
 * @version 1.0 - Jan 17, 2011
 */
public class FleetInfoFrame extends InfoFrameBase {

	/**
	 * Reference to the single instance of the GameData class.
	 */
	private final GameData gameData = GameData.getInstance();

	/**
	 * Reference to the single instance of the <code>GameManager</code> class.
	 */
	private final GameManager gameManager = GameManager.getInstance();

	/**
	 * Reference to the single instance of the <code>FleetManager</code> class.
	 */
	private final FleetManager fleetManager = FleetManager.getInstance();

	/**
	 * An array of Labels to represent the ship names for each design type.
	 */
	private final UILabel[] shipNameLabel = new UILabel[ShipType.TYPE_COUNT];

	/**
	 * An array of Labels to represent the ship count for each design type.
	 */
	private final UILabel[] shipCountLabel = new UILabel[ShipType.TYPE_COUNT];

	/**
	 * The button to remove all ships of this type from the fleet.
	 */
	private final UIButton[] minimizeButton = new UIButton[ShipType.TYPE_COUNT];

	/**
	 * The button to remove one ship of this type from the fleet.
	 */
	private final UIButton[] decrementButton = new UIButton[ShipType.TYPE_COUNT];

	/**
	 * The button to add one ship of this type to the fleet.
	 */
	private final UIButton[] incrementButton = new UIButton[ShipType.TYPE_COUNT];

	/**
	 * The button to put back all ships of this type to the fleet.
	 */
	private final UIButton[] maximizeButton = new UIButton[ShipType.TYPE_COUNT];

	/**
	 * The text label for displaying the current fleet name.
	 */
	private final UILabel fleetNameValueLabel;

	/**
	 * The text label for displaying the current destination.
	 */
	private final UILabel destinationValueLabel;

	/**
	 * The text label for displaying the distance to the current destination.
	 */
	private final UILabel distanceValueLabel;

	/**
	 * The text label for displaying the warp speed of the whole fleet.
	 */
	private final UILabel turnsValueLabel;

	/**
	 * The text label for displaying the warp speed of the whole fleet.
	 */
	private final UILabel warpSpeedValueLabel;

	/**
	 * The text label for displaying the maximum range of the whole fleet.
	 */
	private final UILabel maxRangeValueLabel;

	/**
	 * The button to close the Fleet Deployment Panel.
	 */
	private final UIButton closeButton;

	/**
	 * The button to reset any changes to the fleet composition or cancel fleet orders.
	 */
	private final UIButton resetButton;

	/**
	 * The button to confirm the fleet split and/or destination.
	 */
	private final UIButton sendButton;

	/**
	 * An array for indexing the actual ship design index at the current row index on the panel.
	 */
	private final int[] shipTypeIndex = new int[ShipType.TYPE_COUNT];

	/**
	 * Holds the fleet composition while doing fleet assignment.
	 */
	private final Fleet tempFleet = new Fleet();

	/**
	 * Holds the distance to the destination star while doing fleet assignment.
	 */
	private int tempDistance;

	/**
	 * Holds the number of turns to the destination star while doing fleet assignment.
	 */
	private int tempTurns;

	/**
	 * Holds the current Fleet object.
	 */
	private Fleet currentFleet;

	/**
	 * Default no-arg constructor.
	 * 
	 * @param title
	 */
	public FleetInfoFrame() {
		super("Fleet Deployment", new SolidBackdrop(GameData.COLOR_CHOCOLATE));

		infoPanel.setLayout(new RowLayout(false));

		/*
		 * TOP PANEL
		 */
		final UIPanel grid1 = new UIPanel();
		grid1.setLayout(new AnchorLayout());
		grid1.setBorder(HudManager.SOLID_BORDER_TWO);
		grid1.setMargin(HudManager.MARGINS_ZERO);
		grid1.setPadding(HudManager.PADDING_2000);
		infoPanel.add(grid1);

		/*
		 * Fleet name
		 */
//		final UILabel fleetNameNameLabel = new UILabel("[b]Name: [/b]");
		final UILabel fleetNameNameLabel = new UILabel("Name: ");
		fleetNameNameLabel.setStyledText(true);
		fleetNameNameLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 4, -6));
		grid1.add(fleetNameNameLabel);

		fleetNameValueLabel = new UILabel("");
		fleetNameValueLabel.setAlignment(Alignment.TOP_LEFT);
		fleetNameValueLabel.setStyledText(true);
		fleetNameValueLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 60, -6));
		grid1.add(fleetNameValueLabel);

		/*
		 * Fleet destination
		 */
//		final UILabel destinationNameLabel = new UILabel("[b]Destination:[/b]");
		final UILabel destinationNameLabel = new UILabel("Destination:");
		destinationNameLabel.setStyledText(true);
		destinationNameLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 128, -6));
		grid1.add(destinationNameLabel);

		destinationValueLabel = new UILabel("");
		destinationValueLabel.setStyledText(true);
		destinationValueLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 219, -6));
		grid1.add(destinationValueLabel);

		/*
		 * Fleet max speed
		 */
//		final UILabel warpSpeedNameLabel = new UILabel("[b]Speed: [/b]");
		final UILabel warpSpeedNameLabel = new UILabel("Speed: ");
		warpSpeedNameLabel.setStyledText(true);
		warpSpeedNameLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 4, -24));
		grid1.add(warpSpeedNameLabel);

		warpSpeedValueLabel = new UILabel("");
		warpSpeedValueLabel.setStyledText(true);
		warpSpeedValueLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 60, -24));
		grid1.add(warpSpeedValueLabel);

		/*
		 * Distance to destination
		 */
//		final UILabel distanceNameLabel = new UILabel("[b]Distance:[/b]");
		final UILabel distanceNameLabel = new UILabel("Distance:");
		distanceNameLabel.setStyledText(true);
		distanceNameLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 128, -24));
		grid1.add(distanceNameLabel);

		distanceValueLabel = new UILabel("");
		distanceValueLabel.setStyledText(true);
		distanceValueLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 219, -24));
		grid1.add(distanceValueLabel);

		/*
		 * Fleet max range
		 */
//		final UILabel maxRangeNameLabel = new UILabel("[b]Range: [/b]");
		final UILabel maxRangeNameLabel = new UILabel("Range: ");
		maxRangeNameLabel.setStyledText(true);
		maxRangeNameLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 4, -42));
		grid1.add(maxRangeNameLabel);

		maxRangeValueLabel = new UILabel("");
		maxRangeValueLabel.setStyledText(true);
		maxRangeValueLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 60, -42));
		grid1.add(maxRangeValueLabel);

		/*
		 * Number of turns to arrive
		 */
//		final UILabel turnsNameLabel = new UILabel("[b]ETA:[/b]");
		final UILabel turnsNameLabel = new UILabel("ETA:");
		turnsNameLabel.setStyledText(true);
		turnsNameLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 128, -42));
		grid1.add(turnsNameLabel);

		turnsValueLabel = new UILabel("");
		turnsValueLabel.setStyledText(true);
		turnsValueLabel.setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid1, Alignment.TOP_LEFT, 219, -42));
		grid1.add(turnsValueLabel);

		/*
		 * Radio buttons for each ship type name and its count.
		 */
		final UIPanel grid2 = new UIPanel();
		grid2.setLayout(new AnchorLayout());
		grid2.setBorder(HudManager.SOLID_BORDER_TWO);
		grid2.setMargin(HudManager.MARGINS_ZERO);
		grid2.setPadding(HudManager.PADDING_2000);
		infoPanel.add(grid2);

		int ypos = -5;
		for (int i = 0; i < ShipType.TYPE_COUNT; i++) {
			shipNameLabel[i] = new UILabel("");
			shipNameLabel[i].setStyledText(true);
			shipNameLabel[i].setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid2, Alignment.TOP_LEFT, 4, ypos - 4));
			grid2.add(shipNameLabel[i]);

			shipCountLabel[i] = new UILabel("");
			shipCountLabel[i].setStyledText(true);
			shipCountLabel[i].setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid2, Alignment.TOP_LEFT, 128, ypos - 4));
			grid2.add(shipCountLabel[i]);

			minimizeButton[i] = new UIButton("", SUBTEX_ALLOC_BUTTONS[0]);
			minimizeButton[i].setGap(0);
			minimizeButton[i].setMargin(HudManager.MARGINS_ZERO);
			minimizeButton[i].setPadding(HudManager.PADDING_ZERO);
			minimizeButton[i].setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid2, Alignment.TOP_LEFT, 185, ypos));
			minimizeButton[i].addActionListener(new MinimizeButtonActionListener(i));
			grid2.add(minimizeButton[i]);

			decrementButton[i] = new UIButton("", SUBTEX_ALLOC_BUTTONS[1]);
			decrementButton[i].setGap(0);
			decrementButton[i].setMargin(HudManager.MARGINS_ZERO);
			decrementButton[i].setPadding(HudManager.PADDING_ZERO);
			decrementButton[i].setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid2, Alignment.TOP_LEFT, 217, ypos));
			decrementButton[i].addActionListener(new DecrementButtonActionListener(i));
			grid2.add(decrementButton[i]);

			incrementButton[i] = new UIButton("", SUBTEX_ALLOC_BUTTONS[2]);
			incrementButton[i].setGap(0);
			incrementButton[i].setMargin(HudManager.MARGINS_ZERO);
			incrementButton[i].setPadding(HudManager.PADDING_ZERO);
			incrementButton[i].setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid2, Alignment.TOP_LEFT, 240, ypos));
			incrementButton[i].addActionListener(new IncrementButtonActionListener(i));
			grid2.add(incrementButton[i]);

			maximizeButton[i] = new UIButton("", SUBTEX_ALLOC_BUTTONS[3]);
			maximizeButton[i].setGap(0);
			maximizeButton[i].setMargin(HudManager.MARGINS_ZERO);
			maximizeButton[i].setPadding(HudManager.PADDING_ZERO);
			maximizeButton[i].setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, grid2, Alignment.TOP_LEFT, 263, ypos));
			maximizeButton[i].addActionListener(new MaximizeButtonActionListener(i));
			grid2.add(maximizeButton[i]);

			ypos -= 26;
		}

		/*
		 * Close, Reset, Send buttons
		 */
		final UIPanel hpanel2 = new UIPanel();
		hpanel2.setLayout(new RowLayout(true));
		hpanel2.setBorder(HudManager.SOLID_BORDER_TWO);
		hpanel2.setMargin(HudManager.MARGINS_FOUR);
		hpanel2.setPadding(HudManager.PADDING_TWO);
		infoPanel.add(hpanel2);

//		closeButton = new UIButton("[b]Close[/b]");
		closeButton = new UIButton("Close");
		closeButton.setStyledText(true);
		closeButton.addActionListener(new CloseButtonActionListener());
		hpanel2.add(closeButton);

//		resetButton = new UIButton("[b]Reset[/b]");
		resetButton = new UIButton("Reset");
		resetButton.setStyledText(true);
		resetButton.setEnabled(false);
		resetButton.addActionListener(new ResetButtonActionListener());
		hpanel2.add(resetButton);

//		sendButton = new UIButton("[b]Send[/b]");
		sendButton = new UIButton("Send");
		sendButton.setStyledText(true);
		sendButton.setEnabled(false);
		sendButton.addActionListener(new SendButtonActionListener());
		hpanel2.add(sendButton);
	}

	/**
	 * Updates the widgets based on the selected fleet and destination star. Comes here when the player picks a fleet.
	 */
	public void init() {
		logger.info("Inside FleetInfo.init() -- " + gameData.currentFleetNode.status);

		currentFleet = gameData.currentFleetNode.getFleet();
//		this.backDrop.setColor(GameData.RACE_COLORS[currentFleet.owner.bannerIndex]);

		/*
		 * Sets the fleet name, warp speed and max range labels.
		 */
//		fleetNameValueLabel.setText("[b][c=#00a]" + currentFleet.name + "[/c][/b]");
//		warpSpeedValueLabel.setText("[b][c=#00a]" + currentFleet.warpSpeed + "[/c][/b]");
//		maxRangeValueLabel.setText("[b][c=#00a]" + currentFleet.maxRange + "[/c][/b]");
		fleetNameValueLabel.setText("[c=#00a]" + currentFleet.name + "[/c]");
		warpSpeedValueLabel.setText("[c=#00a]" + currentFleet.warpSpeed + "[/c]");
		maxRangeValueLabel.setText("[c=#00a]" + currentFleet.maxRange + "[/c]");

		/*
		 * Different look based on current status.
		 */
		switch (gameData.currentFleetNode.status) {
		case ORBITING:
			logger.info("Resetting fleet assignment for: " + gameData.currentFleetNode);

			/*
			 * Enables/disables the appropriate rows and sets the labels accordingly.
			 */
			setShipDisplay(true);

			/*
			 * Make a copy of the current fleet. This will be the working copy, the original fleet is untouched.
			 */
			tempFleet.copy(currentFleet);

			/*
			 * Also reset the max speed and range of the fleet.
			 */
			updateSpeedAndRange();

			/*
			 * Clears the destination, distance and number of turns fields.
			 */
			clearDestinationDistanceAndETA();

			break;

		case DEPARTING:
			/*
			 * Enables/disables the appropriate rows and sets the labels accordingly. No reassigment allowed on departing fleets unless their orders are cancelled first.
			 */
			setShipDisplay(false);

			/*
			 * Sets the destination, distance and number of turns fields.
			 */
			gameData.currentDestination = gameData.currentFleetNode.destination;
//			destinationValueLabel.setText("[b]" + ((gameData.currentDestination.getStar().explored) ? "[c=#00a]" + gameData.currentDestination.getName() : "[c=#a00]Unknown") + "[/c][/b]");
			destinationValueLabel.setText(((gameData.currentDestination.getStar().explored) ? "[c=#00a]" + gameData.currentDestination.getName() : "[c=#a00]Unknown") + "[/c]");
			tempDistance = gameData.currentFleetNode.distanceToDestination;
//			distanceValueLabel.setText("[b][c=#00a]" + tempDistance + ((tempDistance > 1) ? " parsecs" : " parsec") + "[/c][/b]");
			distanceValueLabel.setText("[c=#00a]" + tempDistance + ((tempDistance > 1) ? " parsecs" : " parsec") + "[/c]");
			tempTurns = gameData.currentFleetNode.turnsToArrive;
//			turnsValueLabel.setText("[b][c=#00a]" + tempTurns + ((tempTurns > 1) ? " turns" : " turn") + "[/c][/b]");
			turnsValueLabel.setText("[c=#00a]" + tempTurns + ((tempTurns > 1) ? " turns" : " turn") + "[/c]");
			resetButton.setEnabled(true);
			sendButton.setEnabled(false);

			/*
			 * Show the fleet route.
			 */
			gameManager.fleetRoute.show(gameData.currentFleetNode, gameData.currentDestination, true);

			break;

		default: // ENROUTE
			/*
			 * Enables/disables the appropriate rows and sets the labels accordingly. No reassigment or cancelling orders allowed on enroute fleets.
			 */
			setShipDisplay(false);

			/*
			 * Sets the destination, distance and number of turns fields.
			 */
			gameData.currentDestination = gameData.currentFleetNode.destination;
//			destinationValueLabel.setText("[b]" + ((gameData.currentDestination.getStar().explored) ? "[c=#00a]" + gameData.currentDestination.getName() : "[c=#a00]Unknown") + "[/c][/b]");
			destinationValueLabel.setText(((gameData.currentDestination.getStar().explored) ? "[c=#00a]" + gameData.currentDestination.getName() : "[c=#a00]Unknown") + "[/c]");
			tempDistance = gameData.currentFleetNode.distanceToDestination;
//			distanceValueLabel.setText("[b][c=#00a]" + tempDistance + ((tempDistance > 1) ? " parsecs" : " parsec") + "[/c][/b]");
			distanceValueLabel.setText("[c=#00a]" + tempDistance + ((tempDistance > 1) ? " parsecs" : " parsec") + "[/c]");
			tempTurns = gameData.currentFleetNode.turnsToArrive;
//			turnsValueLabel.setText("[b][c=#00a]" + tempTurns + ((tempTurns > 1) ? " turns" : " turn") + "[/c][/b]");
			turnsValueLabel.setText("[c=#00a]" + tempTurns + ((tempTurns > 1) ? " turns" : " turn") + "[/c]");
			resetButton.setEnabled(false);
			sendButton.setEnabled(false);

			/*
			 * Show the fleet route.
			 */
			gameManager.fleetRoute.show(gameData.currentFleetNode, gameData.currentDestination, true);
		}

		updateAndpack();

		logger.info("Leaving FleetInfo.init()");
	}

	/**
	 * Updates the widgets based on the selected destination star. Comes here when the player picks a destination for the fleet.
	 */
	public void update() {
		logger.info("Inside FleetInfo.update()");

		/*
		 * Ignore if the fleet is already enroute or departing the star.
		 */
		if (gameData.currentFleetNode.status != FleetStatus.ORBITING) {
			logger.info("NOT IN ORBIT");
			return;
		}

		/*
		 * Player clicked on the orbiting star. Reset the fleet deployment.
		 */
		if (gameData.currentDestination == gameData.currentFleetNode.orbiting) {
			logger.info("RESETTING FLEET ORDERS");
			clearDestinationDistanceAndETA(); // Clears the destination, distance and number of turns fields.
			return;
		}

		logger.info("UPDATING FLEET INFO");

		/*
		 * Set the destination star name (or UNKNOWN if the star has not been explored).
		 */
//		destinationValueLabel.setText("[b]" + ((gameData.currentDestination.getStar().explored) ? "[c=#00a]" + gameData.currentDestination.getName() : "[c=#a00]Unknown") + "[/c][/b]");
		destinationValueLabel.setText(((gameData.currentDestination.getStar().explored) ? "[c=#00a]" + gameData.currentDestination.getName() : "[c=#a00]Unknown") + "[/c]");

		/*
		 * Calculate the distance to the destination star. This is not necessarily the same as the range value of this star (which is its distance from the NEAREST player colony). Also update the ETA
		 * (number of turns to arrive at the destination).
		 */
		tempDistance = HexGridUtils.distance(gameData.currentFleetNode.tile, gameData.currentDestination.tile);
//		distanceValueLabel.setText("[b][c=#00a]" + tempDistance + ((tempDistance > 1) ? " parsecs" : " parsec") + "[/c][/b]");
		distanceValueLabel.setText("[c=#00a]" + tempDistance + ((tempDistance > 1) ? " parsecs" : " parsec") + "[/c]");
		updateETA();
		resetButton.setEnabled(false);

		updateAndpack();

		logger.info("Leaving FleetInfo.update()");
	}

	/**
	 * Sets the ship type names and their count. If a ship type is not available, disable its button and label.
	 * 
	 * @param enabled - <code>true</code> to enable the allocation buttons; <code>false</code> otherwise
	 */
	private void setShipDisplay(boolean enabled) {
		int row = 0;
		for (int index = 0; index < ShipType.TYPE_COUNT; index++) {
			if (currentFleet.shipCount[index] > 0) {
				shipTypeIndex[row] = index;
//				shipNameLabel[row].setText("[b]" + currentFleet.owner.shipTypes[index].name + "[/b]");
				shipNameLabel[row].setText(currentFleet.owner.shipTypes[index].name);
				shipNameLabel[row].setVisible(true);
//				shipCountLabel[row].setText("[b][c=#00a](" + currentFleet.shipCount[index] + ")[/c][/b]");
				shipCountLabel[row].setText("[c=#00a](" + currentFleet.shipCount[index] + ")[/c]");
				shipCountLabel[row].setVisible(true);
				minimizeButton[row].setVisible(enabled);
				maximizeButton[row].setVisible(enabled);
				decrementButton[row].setVisible(enabled);
				incrementButton[row].setVisible(enabled);
				row++;
			}
		}
		while (row < ShipType.TYPE_COUNT) {
			shipTypeIndex[row] = -1;
			shipNameLabel[row].setText("");
			shipNameLabel[row].setVisible(false);
			shipCountLabel[row].setText("");
			shipCountLabel[row].setVisible(false);
			minimizeButton[row].setVisible(false);
			maximizeButton[row].setVisible(false);
			decrementButton[row].setVisible(false);
			incrementButton[row].setVisible(false);
			row++;
		}
	}

	/**
	 * Update the temporary fleet's max range and speed.
	 */
	private void updateSpeedAndRange() {
		if (tempFleet.maxRange == Integer.MAX_VALUE) { // no ships in the fleet
			maxRangeValueLabel.setText("");
			warpSpeedValueLabel.setText("");
			tempTurns = 0;
			turnsValueLabel.setText("");
			sendButton.setEnabled(false);
		} else {
//			maxRangeValueLabel.setText("[b][c=#00a]" + tempFleet.maxRange + "[/c][/b]");
//			warpSpeedValueLabel.setText("[b][c=#00a]" + tempFleet.warpSpeed + "[/c][/b]");
			maxRangeValueLabel.setText("[c=#00a]" + tempFleet.maxRange + "[/c]");
			warpSpeedValueLabel.setText("[c=#00a]" + tempFleet.warpSpeed + "[/c]");
			if (gameData.currentDestination != null) {
				updateETA();
			}
		}
	}

	/**
	 * Calculate how many turns to arrive at the destination star and update the ETA and route accordingly.
	 */
	private void updateETA() {
		boolean inRange = gameData.currentDestination.range <= tempFleet.maxRange;
		if (inRange) {
			tempTurns = (int) Math.ceil(((float) tempDistance) / ((float) tempFleet.warpSpeed));
//			turnsValueLabel.setText("[b][c=#00a]" + tempTurns + ((tempTurns > 1) ? " turns" : " turn") + "[/c][/b]");
			turnsValueLabel.setText("[c=#00a]" + tempTurns + ((tempTurns > 1) ? " turns" : " turn") + "[/c]");
			sendButton.setEnabled(true);
		} else {
			tempTurns = 0;
//			turnsValueLabel.setText("[b][c=#a00]Out of range (" + gameData.currentDestination.range + ")[/c][/b]");
			turnsValueLabel.setText("[c=#a00]Out of range (" + gameData.currentDestination.range + ")[/c]");
			sendButton.setEnabled(false);
		}

		/*
		 * Show the fleet route.
		 */
		logger.info("fleetinfo.update -- showing fleet route: inrange = " + inRange);
		gameManager.fleetRoute.show(gameData.currentFleetNode, gameData.currentDestination, inRange);
	}

	/**
	 * The star where this fleet is orbiting was selected. Clear the destination, distance and num turns fields.
	 */
	private void clearDestinationDistanceAndETA() {
		gameData.currentDestination = null;
		tempDistance = 0;
		tempTurns = 0;
//		destinationValueLabel.setText("[b][c=#0a0](In Orbit)[/c][/b]");
		destinationValueLabel.setText("[c=#0a0](In Orbit)[/c]");
		distanceValueLabel.setText("");
		turnsValueLabel.setText("");
		resetButton.setEnabled(false);
		sendButton.setEnabled(false);

		/*
		 * Hide the fleet route.
		 */
		gameManager.fleetRoute.hide();
	}

	/**
	 * The Minimize button was clicked. Clear all count for the selected ship type.
	 */
	private final class MinimizeButtonActionListener implements ActionListener {
		final int row;

		public MinimizeButtonActionListener(int row) {
			this.row = row;
		}

		public void actionPerformed(ActionEvent event) {
			int typeIndex = shipTypeIndex[row];
			if (tempFleet.shipCount[typeIndex] > 0) {
				boolean changed = tempFleet.subtractShips(typeIndex, tempFleet.shipCount[typeIndex]);
				if (changed) {
					updateSpeedAndRange();
				}
//				shipCountLabel[row].setText("[b][c=#00a](0)[/c][/b]");
				shipCountLabel[row].setText("[c=#00a](0)[/c]");
				resetButton.setEnabled(true);
			}
		}
	}

	/**
	 * The Maximize button was clicked. Set the count for the selected ship type to the original value.
	 */
	private final class MaximizeButtonActionListener implements ActionListener {
		final int row;

		public MaximizeButtonActionListener(int row) {
			this.row = row;
		}

		public void actionPerformed(ActionEvent event) {
			int shipIndex = shipTypeIndex[row];
			if (tempFleet.shipCount[shipIndex] < currentFleet.shipCount[shipIndex]) {
				boolean changed = tempFleet.addShips(shipIndex, currentFleet.shipCount[shipIndex] - tempFleet.shipCount[shipIndex]);
				if (changed) {
					updateSpeedAndRange();
				}
//				shipCountLabel[row].setText("[b][c=#00a](" + tempFleet.shipCount[shipIndex] + ")[/c][/b]");
				shipCountLabel[row].setText("[c=#00a](" + tempFleet.shipCount[shipIndex] + ")[/c]");
				resetButton.setEnabled(true);
			}
		}
	}

	/**
	 * The Decrement button was clicked. Decrease the count for the selected ship type by 1.
	 */
	private final class DecrementButtonActionListener implements ActionListener {
		final int row;

		public DecrementButtonActionListener(int row) {
			this.row = row;
		}

		public void actionPerformed(ActionEvent event) {
			int shipIndex = shipTypeIndex[row];
			if (tempFleet.shipCount[shipIndex] > 0) {
				boolean changed = tempFleet.subtractShips(shipIndex, 1);
				if (changed) {
					updateSpeedAndRange();
				}
//				shipCountLabel[row].setText("[b][c=#00a](" + tempFleet.shipCount[shipIndex] + ")[/c][/b]");
				shipCountLabel[row].setText("[c=#00a](" + tempFleet.shipCount[shipIndex] + ")[/c]");
				resetButton.setEnabled(true);
			}
		}
	}

	/**
	 * The Increment button was clicked. Increase the count for the selected ship type by 1.
	 */
	private final class IncrementButtonActionListener implements ActionListener {
		final int row;

		public IncrementButtonActionListener(int row) {
			this.row = row;
		}

		public void actionPerformed(ActionEvent event) {
			int shipIndex = shipTypeIndex[row];
			if (tempFleet.shipCount[shipIndex] < currentFleet.shipCount[shipIndex]) {
				boolean changed = tempFleet.addShips(shipIndex, 1);
				if (changed) {
					updateSpeedAndRange();
				}
//				shipCountLabel[row].setText("[b][c=#00a](" + tempFleet.shipCount[shipIndex] + ")[/c][/b]");
				shipCountLabel[row].setText("[c=#00a](" + tempFleet.shipCount[shipIndex] + ")[/c]");
				resetButton.setEnabled(true);
			}
		}
	}

	/**
	 * The Close button was clicked. Close the FleetInfoFrame and show the StarInfoPanel.
	 */
	private final class CloseButtonActionListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			logger.info("Close Button clicked");

			/*
			 * Close this panel.
			 */
			closePanel();
		}
	}

	/**
	 * The Reset button was clicked. Reset the fleet changes or cancels the fleet order.
	 */
	private final class ResetButtonActionListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			logger.info("Reset Button clicked");
			if (gameData.currentFleetNode.status == FleetStatus.ORBITING) {
				/*
				 * Enables/disables the appropriate rows and sets the labels accordingly.
				 */
				setShipDisplay(true);

				/*
				 * Make a copy of the current fleet. This will be the working copy, the original fleet is untouched.
				 */
				tempFleet.copy(currentFleet);

				/*
				 * Also reset the max speed and range of the fleet.
				 */
				updateSpeedAndRange();

				/*
				 * Clears the destination, distance and number of turns fields.
				 */
				clearDestinationDistanceAndETA();
			}
			else { // FleetStatus.DEPARTING
				/*
				 * Remove this fleet from the departing list.
				 */
				fleetManager.removeFleetFromDepartingList(gameData.currentFleetNode);

				/*
				 * Add fleet to the orbiting list, possibly merging with an existing fleet there.
				 */
				gameData.currentFleetNode = fleetManager.putFleetInOrbit(gameData.currentFleetNode.orbiting, gameData.currentFleetNode);

				/*
				 * Close this panel.
				 */
				closePanel();
			}
		}
	}

	/**
	 * The Send button was clicked. Accept the fleet orders to send the fleet on its way.
	 */
	private final class SendButtonActionListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			logger.info("Send Button clicked");

			/*
			 * Accept the fleet orders (Only in ORBITING state).
			 */
			acceptFleetOrders();

			/*
			 * Close this panel.
			 */
			closePanel();
		}
	}

	/**
	 * Only in ORBITING state -- Comes here if the player accepts the fleet orders. The fleet to be sent is in "tempFleet".
	 */
	private void acceptFleetOrders() {
		/*
		 * The whole fleet is to be sent to the destination.
		 */
		if (tempFleet.equals(currentFleet)) { // all ship counts are the same
			/*
			 * Remove this fleet from the list of fleets orbiting this star.
			 */
			fleetManager.removeFleetFromOrbitingList(gameData.currentFleetNode.orbiting, gameData.currentFleetNode);

			/*
			 * Update the properties for this fleet. (x, y, orbiting -- no change)
			 */
			gameData.currentFleetNode.status = FleetStatus.DEPARTING;
			gameData.currentFleetNode.destination = gameData.currentDestination;
			gameData.currentFleetNode.distanceToDestination = tempDistance;
			gameData.currentFleetNode.turnsToArrive = tempTurns;

			/*
			 * Add this fleet to the list of fleets departing this star.
			 */
			fleetManager.addFleetToDepartingList(gameData.currentFleetNode.orbiting, gameData.currentFleetNode);
		}

		/*
		 * Split the current fleet into two and make the new fleet the current fleet,
		 */
		else {
			/*
			 * Create a new fleet node to hold the newly split fleet. The new fleet is a copy of "tempFleet".
			 */
			FleetNode newFleetNode = FleetNode.createFleetNode(gameData.playerRace);
			Fleet newFleet = newFleetNode.getFleet();
			newFleet.copy(tempFleet);
			currentFleet.subtract(tempFleet);

			/*
			 * Update the departing fleet node attributes.
			 */
			newFleetNode.tile.x = gameData.currentFleetNode.tile.x;
			newFleetNode.tile.y = gameData.currentFleetNode.tile.y;
			newFleetNode.status = FleetStatus.DEPARTING;
			newFleetNode.orbiting = gameData.currentFleetNode.orbiting;
			newFleetNode.destination = gameData.currentDestination;
			newFleetNode.distanceToDestination = tempDistance;
			newFleetNode.turnsToArrive = tempTurns;

			/*
			 * Add the new fleet node to the root node.
			 */
			Node parent = gameData.currentFleetNode.getParent();
			parent.attachChild(newFleetNode);

			/*
			 * Add this fleet to the list of fleets departing this star.
			 */
			fleetManager.addFleetToDepartingList(gameData.currentFleetNode.orbiting, newFleetNode);

			/*
			 * Make this FleetNode the current fleet node.
			 */
			gameData.currentFleetNode = newFleetNode;
		}
	}

	/**
	 * Comes here if the player closes the fleet info panel. Highlight the star where the fleet is at unless the fleet is en route.
	 */
	private void closePanel() {
		if (gameData.currentFleetNode.status != FleetNode.FleetStatus.ENROUTE)
			gameData.currentStarNode = gameData.currentFleetNode.orbiting;

		/*
		 * Hide the fleet route.
		 */
		gameManager.fleetRoute.hide();

		gameData.action = Actions.STAR_PICKED;
		gameData.gameState = GameStates.IN_STAR_INFO;
	}

}
